<template><div><h2 id="依赖注入容器-dependency-injection-container" tabindex="-1"><a class="header-anchor" href="#依赖注入容器-dependency-injection-container"><span>依赖注入容器（Dependency Injection Container）</span></a></h2>
<p>依赖注入（Dependency Injection，DI）容器就是一个对象，它知道怎样初始化并配置对象及其依赖的所有对象。<a href="http://martinfowler.com/articles/injection.html" target="_blank" rel="noopener noreferrer">Martin 的文章</a> 已经解释了 DI 容器为什么很有用。这里我们主要讲解 Yii 提供的 DI 容器的使用方法。</p>
<h2 id="依赖注入-di" tabindex="-1"><a class="header-anchor" href="#依赖注入-di"><span>依赖注入（DI）</span></a></h2>
<p>Yii 通过 <code v-pre>yii\di\Container</code> 类提供 DI 容器特性。它支持如下几种类型的依赖注入：</p>
<ul>
<li>构造方法注入;</li>
<li>方法注入;</li>
<li>Setter 和属性注入;</li>
<li>PHP 回调注入.</li>
</ul>
<h3 id="构造方法注入" tabindex="-1"><a class="header-anchor" href="#构造方法注入"><span>构造方法注入</span></a></h3>
<p>在参数类型提示的帮助下，DI 容器实现了构造方法注入。当容器被用于创建一个新对象时，类型提示会告诉它要依赖什么类或接口。容器会尝试获取它所依赖的类或接口的实例，然后通过构造器将其注入新的对象。例如：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">Foo</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">__construct</span><span class="token punctuation">(</span><span class="token class-name type-declaration">Bar</span> <span class="token variable">$bar</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line"><span class="token variable">$foo</span> <span class="token operator">=</span> <span class="token variable">$container</span><span class="token operator">-></span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'Foo'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token comment">// 上面的代码等价于：</span></span>
<span class="line"><span class="token variable">$bar</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Bar</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$foo</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Foo</span><span class="token punctuation">(</span><span class="token variable">$bar</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="方法注入" tabindex="-1"><a class="header-anchor" href="#方法注入"><span>方法注入</span></a></h3>
<p>通常，类的依赖关系传递给构造函数，并且在整个生命周期中都可以在类内部使用。通过方法注入，可以提供仅由类的单个方法需要的依赖关系，并将其传递给构造函数可能不可行，或者可能会在大多数用例中导致太多开销。</p>
<p>类方法可以像下面例子中的 <code v-pre>doSomething()</code> 方法一样定义：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">MyClass</span> <span class="token keyword">extends</span> <span class="token class-name class-name-fully-qualified"><span class="token punctuation">\</span>yii<span class="token punctuation">\</span>base<span class="token punctuation">\</span>Component</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">__construct</span><span class="token punctuation">(</span><span class="token comment">/*Some lightweight dependencies here*/</span><span class="token punctuation">,</span> <span class="token variable">$config</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token comment">// ...</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">doSomething</span><span class="token punctuation">(</span><span class="token variable">$param1</span><span class="token punctuation">,</span> <span class="token class-name class-name-fully-qualified type-declaration"><span class="token punctuation">\</span>my<span class="token punctuation">\</span>heavy<span class="token punctuation">\</span>Dependency</span> <span class="token variable">$something</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token comment">// do something with $something</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>你可以自己通过一个实例 <code v-pre>\my\heavy\Dependency</code> 调用这个方法或使用 [[yii\di\Container::invoke()]] 如下：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$obj</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">MyClass</span><span class="token punctuation">(</span><span class="token comment">/*...*/</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token class-name static-context">Yii</span><span class="token operator">::</span><span class="token variable">$container</span><span class="token operator">-></span><span class="token function">invoke</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token variable">$obj</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'doSomething'</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'param1'</span> <span class="token operator">=></span> <span class="token number">42</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// $something will be provided by the DI container</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="setter-和属性注入" tabindex="-1"><a class="header-anchor" href="#setter-和属性注入"><span>Setter 和属性注入</span></a></h3>
<p>Setter 和属性注入是通过 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/concept-configurations.md" target="_blank" rel="noopener noreferrer">配置</a> 提供支持的。当注册一个依赖或创建一个新对象时，你可以提供一个配置， 该配置会提供给容器用于通过相应的 Setter 或属性注入依赖。例如：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">use</span> <span class="token package">yii<span class="token punctuation">\</span>base<span class="token punctuation">\</span>BaseObject</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">Foo</span> <span class="token keyword">extends</span> <span class="token class-name">BaseObject</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token variable">$bar</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">private</span> <span class="token variable">$_qux</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">getQux</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token property">_qux</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">setQux</span><span class="token punctuation">(</span><span class="token class-name type-declaration">Qux</span> <span class="token variable">$qux</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token variable">$this</span><span class="token operator">-></span><span class="token property">_qux</span> <span class="token operator">=</span> <span class="token variable">$qux</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line"><span class="token variable">$container</span><span class="token operator">-></span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'Foo'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span></span>
<span class="line">    <span class="token string single-quoted-string">'bar'</span> <span class="token operator">=></span> <span class="token variable">$container</span><span class="token operator">-></span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'Bar'</span><span class="token punctuation">)</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token string single-quoted-string">'qux'</span> <span class="token operator">=></span> <span class="token variable">$container</span><span class="token operator">-></span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'Qux'</span><span class="token punctuation">)</span><span class="token punctuation">,</span></span>
<span class="line"><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><blockquote>
<p>Info: [[yii\di\Container::get()]] 方法将其第三个参数作为配置数组应用于正在创建的对象。如果该类实现 <code v-pre>yii\base\Configurable</code> 接口（例如 <code v-pre>yii\base\BaseObject</code>），则配置数组将作为最后一个参数传递给类构造函数；否则，将在创建对象<em>后</em>应用该配置。</p>
</blockquote>
<h3 id="php-回调注入" tabindex="-1"><a class="header-anchor" href="#php-回调注入"><span>PHP 回调注入</span></a></h3>
<p>在这种情况下，容器将使用已注册的 PHP 回调来构建类的新实例。每次调用 [[yii\di\Container::get()]] ，相应的回调将被调用。调用方负责解析依赖项，并适当地将它们注入到新创建的对象中。例如,</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$container</span><span class="token operator">-></span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'Foo'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token variable">$foo</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Foo</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Bar</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token comment">// ... 其他初始化 ...</span></span>
<span class="line">    <span class="token keyword">return</span> <span class="token variable">$foo</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token variable">$foo</span> <span class="token operator">=</span> <span class="token variable">$container</span><span class="token operator">-></span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'Foo'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>要省略构建新对象的复杂逻辑，可以使用静态类方法作为可调用的方法。例如，</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">FooBuilder</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">function</span> <span class="token function-definition function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token variable">$foo</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Foo</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Bar</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">        <span class="token comment">// ... 其他初始化 ...</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token variable">$foo</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line"><span class="token variable">$container</span><span class="token operator">-></span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'Foo'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'app\helper\FooBuilder'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'build'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token variable">$foo</span> <span class="token operator">=</span> <span class="token variable">$container</span><span class="token operator">-></span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'Foo'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>这样做的话，想要配置 <code v-pre>Foo</code> 类的人不再需要知道它是如何构建的。</p>
<h2 id="注册依赖关系" tabindex="-1"><a class="header-anchor" href="#注册依赖关系"><span>注册依赖关系</span></a></h2>
<p>可以用 [[yii\di\Container::set()]] 注册依赖关系。注册会用到一个依赖关系名称和一个依赖关系的定义。依赖关系名称可以是一个类名，一个接口名或一个别名。依赖关系的定义可以是一个类名，一个配置数组，或者一个 PHP 回调。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$container</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name class-name-fully-qualified"><span class="token punctuation">\</span>yii<span class="token punctuation">\</span>di<span class="token punctuation">\</span>Container</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 注册一个同类名一样的依赖关系，这个可以省略。</span></span>
<span class="line"><span class="token variable">$container</span><span class="token operator">-></span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'yii\db\Connection'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 注册一个接口</span></span>
<span class="line"><span class="token comment">// 当一个类依赖这个接口时，</span></span>
<span class="line"><span class="token comment">//相应的类会被初始化作为依赖对象。</span></span>
<span class="line"><span class="token variable">$container</span><span class="token operator">-></span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'yii\mail\MailInterface'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'yii\swiftmailer\Mailer'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 注册一个别名。</span></span>
<span class="line"><span class="token comment">// 你可以使用 $container->get('foo') 创建一个 Connection 实例</span></span>
<span class="line"><span class="token variable">$container</span><span class="token operator">-></span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'foo'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'yii\db\Connection'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 通过配置注册一个类</span></span>
<span class="line"><span class="token comment">// 通过 get() 初始化时，配置将会被使用。</span></span>
<span class="line"><span class="token variable">$container</span><span class="token operator">-></span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'yii\db\Connection'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span></span>
<span class="line">    <span class="token string single-quoted-string">'dsn'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'mysql:host=127.0.0.1;dbname=demo'</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token string single-quoted-string">'username'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'root'</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token string single-quoted-string">'password'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">''</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token string single-quoted-string">'charset'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'utf8'</span><span class="token punctuation">,</span></span>
<span class="line"><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 通过类的配置注册一个别名</span></span>
<span class="line"><span class="token comment">// 这种情况下，需要通过一个 “class” 元素指定这个类</span></span>
<span class="line"><span class="token variable">$container</span><span class="token operator">-></span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'db'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span></span>
<span class="line">    <span class="token string single-quoted-string">'class'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'yii\db\Connection'</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token string single-quoted-string">'dsn'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'mysql:host=127.0.0.1;dbname=demo'</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token string single-quoted-string">'username'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'root'</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token string single-quoted-string">'password'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">''</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token string single-quoted-string">'charset'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'utf8'</span><span class="token punctuation">,</span></span>
<span class="line"><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 注册一个 PHP 回调</span></span>
<span class="line"><span class="token comment">// 每次调用 $container->get('db') 时，回调函数都会被执行。</span></span>
<span class="line"><span class="token variable">$container</span><span class="token operator">-></span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'db'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token variable">$container</span><span class="token punctuation">,</span> <span class="token variable">$params</span><span class="token punctuation">,</span> <span class="token variable">$config</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name class-name-fully-qualified"><span class="token punctuation">\</span>yii<span class="token punctuation">\</span>db<span class="token punctuation">\</span>Connection</span><span class="token punctuation">(</span><span class="token variable">$config</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 注册一个组件实例</span></span>
<span class="line"><span class="token comment">// $container->get('pageCache') 每次被调用时都会返回同一个实例。</span></span>
<span class="line"><span class="token variable">$container</span><span class="token operator">-></span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'pageCache'</span><span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">FileCache</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><blockquote>
<p>Tip: 如果依赖关系名称和依赖关系的定义相同，则不需要通过 DI 容器注册该依赖关系。</p>
</blockquote>
<p>通过 <code v-pre>set()</code> 注册的依赖关系，在每次使用时都会产生一个新实例。可以使用 [[yii\di\Container::setSingleton()]] 注册一个单例的依赖关系：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$container</span><span class="token operator">-></span><span class="token function">setSingleton</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'yii\db\Connection'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span></span>
<span class="line">    <span class="token string single-quoted-string">'dsn'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'mysql:host=127.0.0.1;dbname=demo'</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token string single-quoted-string">'username'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'root'</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token string single-quoted-string">'password'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">''</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token string single-quoted-string">'charset'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'utf8'</span><span class="token punctuation">,</span></span>
<span class="line"><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2 id="解决依赖关系" tabindex="-1"><a class="header-anchor" href="#解决依赖关系"><span>解决依赖关系</span></a></h2>
<p>注册依赖关系后，就可以使用 DI 容器创建新对象了。容器会自动解决依赖关系，将依赖实例化并注入新创建的对象。依赖关系的解决是递归的，如果一个依赖关系中还有其他依赖关系，则这些依赖关系都会被自动解决。</p>
<p>可以使用 [[yii\di\Container::get()]] 创建新的对象。该方法接收一个依赖关系名称，它可以是一个类名，一个接口名或一个别名。依赖关系名或许是通过 <code v-pre>set()</code> 或 <code v-pre>setSingleton()</code> 注册的。你可以随意地提供一个类的构造器参数列表和一个 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/concept-configurations.md" target="_blank" rel="noopener noreferrer">配置</a> 用于配置新创建的对象。</p>
<p>例如：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// "db" 是前面定义过的一个别名</span></span>
<span class="line"><span class="token variable">$db</span> <span class="token operator">=</span> <span class="token variable">$container</span><span class="token operator">-></span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'db'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 等价于： $engine = new \app\components\SearchEngine($apiKey, ['type' => 1]);</span></span>
<span class="line"><span class="token variable">$engine</span> <span class="token operator">=</span> <span class="token variable">$container</span><span class="token operator">-></span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'app\components\SearchEngine'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token variable">$apiKey</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'type'</span> <span class="token operator">=></span> <span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>代码背后，DI 容器做了比创建对象多的多的工作。容器首先将检查类的构造方法，找出依赖的类或接口名，然后自动递归解决这些依赖关系。</p>
<p>如下代码展示了一个更复杂的示例。<code v-pre>UserLister</code> 类依赖一个实现了 <code v-pre>UserFinderInterface</code> 接口的对象； <code v-pre>UserFinder</code> 类实现了这个接口，并依赖于一个 <code v-pre>Connection</code> 对象。所有这些依赖关系都是通过类构造器参数的类型提示定义的。通过属性依赖关系的注册，DI 容器可以自动解决这些依赖关系并能通过一个 简单的 <code v-pre>get('userLister')</code> 调用创建一个新的 <code v-pre>UserLister</code> 实例。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">namespace</span> <span class="token package">app<span class="token punctuation">\</span>models</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">use</span> <span class="token package">yii<span class="token punctuation">\</span>base<span class="token punctuation">\</span>BaseObject</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token keyword">use</span> <span class="token package">yii<span class="token punctuation">\</span>db<span class="token punctuation">\</span>Connection</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token keyword">use</span> <span class="token package">yii<span class="token punctuation">\</span>di<span class="token punctuation">\</span>Container</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">interface</span> <span class="token class-name-definition class-name">UserFinderInterface</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">function</span> <span class="token function-definition function">findUser</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">UserFinder</span> <span class="token keyword">extends</span> <span class="token class-name">BaseObject</span> <span class="token keyword">implements</span> <span class="token class-name">UserFinderInterface</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token variable">$db</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">__construct</span><span class="token punctuation">(</span><span class="token class-name type-declaration">Connection</span> <span class="token variable">$db</span><span class="token punctuation">,</span> <span class="token variable">$config</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token variable">$this</span><span class="token operator">-></span><span class="token property">db</span> <span class="token operator">=</span> <span class="token variable">$db</span><span class="token punctuation">;</span></span>
<span class="line">        <span class="token keyword static-context">parent</span><span class="token operator">::</span><span class="token function">__construct</span><span class="token punctuation">(</span><span class="token variable">$config</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">findUser</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">UserLister</span> <span class="token keyword">extends</span> <span class="token class-name">BaseObject</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">public</span> <span class="token variable">$finder</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">__construct</span><span class="token punctuation">(</span><span class="token class-name type-declaration">UserFinderInterface</span> <span class="token variable">$finder</span><span class="token punctuation">,</span> <span class="token variable">$config</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token variable">$this</span><span class="token operator">-></span><span class="token property">finder</span> <span class="token operator">=</span> <span class="token variable">$finder</span><span class="token punctuation">;</span></span>
<span class="line">        <span class="token keyword static-context">parent</span><span class="token operator">::</span><span class="token function">__construct</span><span class="token punctuation">(</span><span class="token variable">$config</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line"><span class="token variable">$container</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Container</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$container</span><span class="token operator">-></span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'yii\db\Connection'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span></span>
<span class="line">    <span class="token string single-quoted-string">'dsn'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'...'</span><span class="token punctuation">,</span></span>
<span class="line"><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$container</span><span class="token operator">-></span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'app\models\UserFinderInterface'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span></span>
<span class="line">    <span class="token string single-quoted-string">'class'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'app\models\UserFinder'</span><span class="token punctuation">,</span></span>
<span class="line"><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$container</span><span class="token operator">-></span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'userLister'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'app\models\UserLister'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token variable">$lister</span> <span class="token operator">=</span> <span class="token variable">$container</span><span class="token operator">-></span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'userLister'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 等价于:</span></span>
<span class="line"></span>
<span class="line"><span class="token variable">$db</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name class-name-fully-qualified"><span class="token punctuation">\</span>yii<span class="token punctuation">\</span>db<span class="token punctuation">\</span>Connection</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'dsn'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'...'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$finder</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">UserFinder</span><span class="token punctuation">(</span><span class="token variable">$db</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token variable">$lister</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">UserLister</span><span class="token punctuation">(</span><span class="token variable">$finder</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2 id="实践运用" tabindex="-1"><a class="header-anchor" href="#实践运用"><span>实践运用</span></a></h2>
<p>当在应用程序的 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/structure-entry-scripts.md" target="_blank" rel="noopener noreferrer">入口脚本</a> 中引入 <code v-pre>Yii.php</code> 文件时，Yii 就创建了一个 DI 容器。这个 DI 容器可以通过 [[Yii::$container]] 访问。当调用 [[Yii::createObject()]] 时，此方法实际上会调用这个容器的 [[yii\di\Container::get()]] 方法创建新对象。如上所述，DI 容器会自动解决依赖关系（如果有）并将其注入新创建的对象中。因为 Yii 在其多数核心代码中都使用了 [[Yii::createObject()]] 创建新对象，所以你可以通过 [[Yii::$container]] 全局性地自定义这些对象。</p>
<p>例如，你可以全局性自定义 [[yii\widgets\LinkPager]] 中分页按钮的默认数量:</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token class-name class-name-fully-qualified static-context"><span class="token punctuation">\</span>Yii</span><span class="token operator">::</span><span class="token variable">$container</span><span class="token operator">-></span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'yii\widgets\LinkPager'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string single-quoted-string">'maxButtonCount'</span> <span class="token operator">=></span> <span class="token number">5</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><p>这样如果你通过如下代码在一个视图里使用这个挂件，它的 <code v-pre>maxButtonCount</code> 属性就会被初始化为 5 而不是类中定义的默认值 10。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">echo</span> <span class="token class-name class-name-fully-qualified static-context"><span class="token punctuation">\</span>yii<span class="token punctuation">\</span>widgets<span class="token punctuation">\</span>LinkPager</span><span class="token operator">::</span><span class="token function">widget</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><p>然而你依然可以覆盖通过 DI 容器设置的值：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">echo</span> <span class="token class-name class-name-fully-qualified static-context"><span class="token punctuation">\</span>yii<span class="token punctuation">\</span>widgets<span class="token punctuation">\</span>LinkPager</span><span class="token operator">::</span><span class="token function">widget</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string single-quoted-string">'maxButtonCount'</span> <span class="token operator">=></span> <span class="token number">20</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><blockquote>
<p>Note: 在部件调用中给出的属性将始终覆盖DI容器中的定义。即使您指定了一个数组，例如 <code v-pre>'options' =&gt; ['id' =&gt; 'mypager']</code> 这些将不会与其他选项合并，而是替换它们。</p>
</blockquote>
<p>另一个例子是借用 DI 容器中自动构造方法注入带来的好处。假设你的控制器类依赖一些其他对象，例如一个旅馆预订服务。你可以通过一个构造器参数声明依赖关系，然后让 DI 容器帮你自动解决这个依赖关系。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">namespace</span> <span class="token package">app<span class="token punctuation">\</span>controllers</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">use</span> <span class="token package">yii<span class="token punctuation">\</span>web<span class="token punctuation">\</span>Controller</span><span class="token punctuation">;</span></span>
<span class="line"><span class="token keyword">use</span> <span class="token package">app<span class="token punctuation">\</span>components<span class="token punctuation">\</span>BookingInterface</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">HotelController</span> <span class="token keyword">extends</span> <span class="token class-name">Controller</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">protected</span> <span class="token variable">$bookingService</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">__construct</span><span class="token punctuation">(</span><span class="token variable">$id</span><span class="token punctuation">,</span> <span class="token variable">$module</span><span class="token punctuation">,</span> <span class="token class-name type-declaration">BookingInterface</span> <span class="token variable">$bookingService</span><span class="token punctuation">,</span> <span class="token variable">$config</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token variable">$this</span><span class="token operator">-></span><span class="token property">bookingService</span> <span class="token operator">=</span> <span class="token variable">$bookingService</span><span class="token punctuation">;</span></span>
<span class="line">        <span class="token keyword static-context">parent</span><span class="token operator">::</span><span class="token function">__construct</span><span class="token punctuation">(</span><span class="token variable">$id</span><span class="token punctuation">,</span> <span class="token variable">$module</span><span class="token punctuation">,</span> <span class="token variable">$config</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>如果你从浏览器中访问这个控制器，你将看到一个报错信息，提醒你 <code v-pre>BookingInterface</code> 无法被实例化。这是因为你需要告诉 DI 容器怎样处理这个依赖关系。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token class-name class-name-fully-qualified static-context"><span class="token punctuation">\</span>Yii</span><span class="token operator">::</span><span class="token variable">$container</span><span class="token operator">-></span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'app\components\BookingInterface'</span><span class="token punctuation">,</span> <span class="token string single-quoted-string">'app\components\BookingService'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><p>现在如果你再次访问这个控制器，一个 <code v-pre>app\components\BookingService</code> 的实例就会被创建并被作为第三个参数注入到控制器的构造器中。</p>
<h2 id="高级实用性" tabindex="-1"><a class="header-anchor" href="#高级实用性"><span>高级实用性</span></a></h2>
<p>比如说我们在 API 应用方面工作：</p>
<ul>
<li>
<p><code v-pre>app\components\Request</code> 类继承了 <code v-pre>yii\web\Request</code> 并提供了额外的功能</p>
</li>
<li>
<p><code v-pre>app\components\Response</code> 类继承了 <code v-pre>yii\web\Response</code> 并且在创建时应该将 <code v-pre>format</code> 属性设置为 <code v-pre>json</code></p>
</li>
<li>
<p><code v-pre>app\storage\FileStorage</code> 和 <code v-pre>app\storage\DocumentsReader</code> 用于处理位于某些文件存储中的文档的某些逻辑：</p>
</li>
</ul>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line">    <span class="token keyword">class</span> <span class="token class-name-definition class-name">FileStorage</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">__construct</span><span class="token punctuation">(</span><span class="token variable">$root</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">            <span class="token comment">// whatever</span></span>
<span class="line">        <span class="token punctuation">}</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">class</span> <span class="token class-name-definition class-name">DocumentsReader</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">__construct</span><span class="token punctuation">(</span><span class="token class-name type-declaration">FileStorage</span> <span class="token variable">$fs</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">            <span class="token comment">// whatever</span></span>
<span class="line">        <span class="token punctuation">}</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>可以一次配置多个定义，将配置数组传递给 [[yii\di\Container::setDefinitions()]] 或 [[yii\di\Container::setSingletons()|]] 方法。 遍历配置数组，将分别为每个对象分别调用 [[yii\di\Container::set()]] 或 [[yii\di\Container::setSingleton()]] 方法。</p>
<p>配置数组格式为：</p>
<ul>
<li><code v-pre>key</code>：类名称，接口名称或别名。 该 key 将作为第一个参数 <code v-pre>$class</code> 传递给 [[yii\di\Container::set()]] 方法。</li>
<li><code v-pre>value</code>：与 <code v-pre>$class</code> 关联的定义。<code v-pre>$definition</code> 参数的值可能在 [[yii\di\Container::set()]] 文档中描述。<code v-pre>$definition</code> 将作为第二个参数传递给 [[set()]] 方法。</li>
</ul>
<p>例如，让我们配置我们的容器以遵循上述要求：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$container</span><span class="token operator">-></span><span class="token function">setDefinitions</span><span class="token punctuation">(</span><span class="token punctuation">[</span></span>
<span class="line">    <span class="token string single-quoted-string">'yii\web\Request'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'app\components\Request'</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token string single-quoted-string">'yii\web\Response'</span> <span class="token operator">=></span> <span class="token punctuation">[</span></span>
<span class="line">        <span class="token string single-quoted-string">'class'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'app\components\Response'</span><span class="token punctuation">,</span></span>
<span class="line">        <span class="token string single-quoted-string">'format'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'json'</span></span>
<span class="line">    <span class="token punctuation">]</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token string single-quoted-string">'app\storage\DocumentsReader'</span> <span class="token operator">=></span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span>
<span class="line">        <span class="token variable">$fs</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name class-name-fully-qualified">app<span class="token punctuation">\</span>storage<span class="token punctuation">\</span>FileStorage</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'/var/tempfiles'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name class-name-fully-qualified">app<span class="token punctuation">\</span>storage<span class="token punctuation">\</span>DocumentsReader</span><span class="token punctuation">(</span><span class="token variable">$fs</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token variable">$reader</span> <span class="token operator">=</span> <span class="token variable">$container</span><span class="token operator">-></span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'app\storage\DocumentsReader'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> </span>
<span class="line"><span class="token comment">// 将按照配置中的描述创建 DocumentReader 对象及其依赖关系</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><blockquote>
<p>Tip: 自 2.0.11 版以后，可以使用应用程序配置以声明式风格配置容器。查看 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/concept-configurations.md" target="_blank" rel="noopener noreferrer">配置</a> 指南文章的 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/concept-configurations.md#application-configurations" target="_blank" rel="noopener noreferrer">应用程序配置</a> 小节。</p>
</blockquote>
<p>一切正常，但如果我们需要创建 <code v-pre>Document Writer</code> 类，我们将复制粘贴创建 <code v-pre>FileStorage</code> 对象的行，这显然不是最聪明的方式。</p>
<p>如 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/concept-di-container.md#resolving-dependencies" target="_blank" rel="noopener noreferrer">解决依赖关系</a> 子节中所述，[[yii\di\Container::set()]] 和 [[yii\di\Container::setSingleton()]] 可以选择将依赖项的构造函数参数作为第三个参数。要设置构造函数参数，可以使用以下配置数组格式：</p>
<ul>
<li><code v-pre>key</code>：类名称，接口名称或别名。该 key 将作为第一个参数 <code v-pre>$class</code> 传递给 [[yii\di\Container::set()]] 方法。</li>
<li><code v-pre>value</code>：两个元素的数组。第一个元素将传递给 [[yii\di\Container::set()]] 方法 作为第二个参数 <code v-pre>$definition</code>，第二个元素为 <code v-pre>$params</code>。</li>
</ul>
<p>让我们来修改我们的例子：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$container</span><span class="token operator">-></span><span class="token function">setDefinitions</span><span class="token punctuation">(</span><span class="token punctuation">[</span></span>
<span class="line">    <span class="token string single-quoted-string">'tempFileStorage'</span> <span class="token operator">=></span> <span class="token punctuation">[</span> <span class="token comment">// 我们为了方便创建了一个别名</span></span>
<span class="line">        <span class="token punctuation">[</span><span class="token string single-quoted-string">'class'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'app\storage\FileStorage'</span><span class="token punctuation">]</span><span class="token punctuation">,</span></span>
<span class="line">        <span class="token punctuation">[</span><span class="token string single-quoted-string">'/var/tempfiles'</span><span class="token punctuation">]</span> <span class="token comment">// 可以从一些配置文件中获取</span></span>
<span class="line">    <span class="token punctuation">]</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token string single-quoted-string">'app\storage\DocumentsReader'</span> <span class="token operator">=></span> <span class="token punctuation">[</span></span>
<span class="line">        <span class="token punctuation">[</span><span class="token string single-quoted-string">'class'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'app\storage\DocumentsReader'</span><span class="token punctuation">]</span><span class="token punctuation">,</span></span>
<span class="line">        <span class="token punctuation">[</span><span class="token class-name static-context">Instance</span><span class="token operator">::</span><span class="token function">of</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'tempFileStorage'</span><span class="token punctuation">)</span><span class="token punctuation">]</span></span>
<span class="line">    <span class="token punctuation">]</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token string single-quoted-string">'app\storage\DocumentsWriter'</span> <span class="token operator">=></span> <span class="token punctuation">[</span></span>
<span class="line">        <span class="token punctuation">[</span><span class="token string single-quoted-string">'class'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'app\storage\DocumentsWriter'</span><span class="token punctuation">]</span><span class="token punctuation">,</span></span>
<span class="line">        <span class="token punctuation">[</span><span class="token class-name static-context">Instance</span><span class="token operator">::</span><span class="token function">of</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'tempFileStorage'</span><span class="token punctuation">)</span><span class="token punctuation">]</span></span>
<span class="line">    <span class="token punctuation">]</span></span>
<span class="line"><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token variable">$reader</span> <span class="token operator">=</span> <span class="token variable">$container</span><span class="token operator">-></span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'app\storage\DocumentsReader'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> </span>
<span class="line"><span class="token comment">// 将与前面示例中的行为完全相同。</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>你可能会注意到 <code v-pre>Instance::of('tempFileStorage')</code> 符号。这意味着，<code v-pre>yii\di\Container</code> 将隐含地提供一个用 <code v-pre>tempFileStorage</code> 名称注册的依赖项， 并将其作为 <code v-pre>app\storage\DocumentsWriter</code> 构造函数的第一个参数传递。</p>
<blockquote>
<p>Note: [[yii\di\Container::setDefinitions()]] 和 [[yii\di\Container::setSingletons()]] 方法从版本 2.0.11 开始可用。</p>
</blockquote>
<p>配置优化的另一个步骤是将某些依赖项注册为单例。通过 [[yii\di\Container::set()]] 注册的依赖项将在每次需要时实例化。某些类在运行时不会更改状态，因此它们可能会被注册为单例以提高应用程序的性能。</p>
<p>一个很好的例子可以是 <code v-pre>app\storage\FileStorage</code> 类，它用一个简单的 API（例如 <code v-pre>$fs-&gt;read()</code>，<code v-pre>$fs-&gt;write()</code>）在文件系统上执行一些操作。这些操作不会更改内部类的状态，因此我们可以创建一次实例并多次使用它。</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$container</span><span class="token operator">-></span><span class="token function">setSingletons</span><span class="token punctuation">(</span><span class="token punctuation">[</span></span>
<span class="line">    <span class="token string single-quoted-string">'tempFileStorage'</span> <span class="token operator">=></span> <span class="token punctuation">[</span></span>
<span class="line">        <span class="token punctuation">[</span><span class="token string single-quoted-string">'class'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'app\storage\FileStorage'</span><span class="token punctuation">]</span><span class="token punctuation">,</span></span>
<span class="line">        <span class="token punctuation">[</span><span class="token string single-quoted-string">'/var/tempfiles'</span><span class="token punctuation">]</span></span>
<span class="line">    <span class="token punctuation">]</span><span class="token punctuation">,</span></span>
<span class="line"><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token variable">$container</span><span class="token operator">-></span><span class="token function">setDefinitions</span><span class="token punctuation">(</span><span class="token punctuation">[</span></span>
<span class="line">    <span class="token string single-quoted-string">'app\storage\DocumentsReader'</span> <span class="token operator">=></span> <span class="token punctuation">[</span></span>
<span class="line">        <span class="token punctuation">[</span><span class="token string single-quoted-string">'class'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'app\storage\DocumentsReader'</span><span class="token punctuation">]</span><span class="token punctuation">,</span></span>
<span class="line">        <span class="token punctuation">[</span><span class="token class-name static-context">Instance</span><span class="token operator">::</span><span class="token function">of</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'tempFileStorage'</span><span class="token punctuation">)</span><span class="token punctuation">]</span></span>
<span class="line">    <span class="token punctuation">]</span><span class="token punctuation">,</span></span>
<span class="line">    <span class="token string single-quoted-string">'app\storage\DocumentsWriter'</span> <span class="token operator">=></span> <span class="token punctuation">[</span></span>
<span class="line">        <span class="token punctuation">[</span><span class="token string single-quoted-string">'class'</span> <span class="token operator">=></span> <span class="token string single-quoted-string">'app\storage\DocumentsWriter'</span><span class="token punctuation">]</span><span class="token punctuation">,</span></span>
<span class="line">        <span class="token punctuation">[</span><span class="token class-name static-context">Instance</span><span class="token operator">::</span><span class="token function">of</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'tempFileStorage'</span><span class="token punctuation">)</span><span class="token punctuation">]</span></span>
<span class="line">    <span class="token punctuation">]</span></span>
<span class="line"><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token variable">$reader</span> <span class="token operator">=</span> <span class="token variable">$container</span><span class="token operator">-></span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'app\storage\DocumentsReader'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2 id="注册依赖关系的时机" tabindex="-1"><a class="header-anchor" href="#注册依赖关系的时机"><span>注册依赖关系的时机</span></a></h2>
<p>由于依赖关系在创建新对象时需要解决，因此它们的注册应该尽早完成。 如下是推荐的实践：</p>
<ul>
<li>如果你是一个应用程序的开发者，你可以在应用程序的 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/structure-entry-scripts.md" target="_blank" rel="noopener noreferrer">入口脚本</a> 或者被入口脚本引入的脚本中注册依赖关系。</li>
<li>如果你是一个可再分发 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/structure-extensions.md" target="_blank" rel="noopener noreferrer">扩展</a>的开发者，你可以将依赖关系注册到扩展的引导类中。</li>
</ul>
<h2 id="总结" tabindex="-1"><a class="header-anchor" href="#总结"><span>总结</span></a></h2>
<p>依赖注入 和 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/concept-service-locator.md" target="_blank" rel="noopener noreferrer">服务定位器</a> 都是流行的设计模式，它们使你可以用充分解耦且更利于测试的风格构建软件。强烈推荐你阅读 <a href="http://martinfowler.com/articles/injection.html" target="_blank" rel="noopener noreferrer">Martin 的文章</a> ，对依赖注入和服务定位器有个更深入的理解。</p>
<p>Yii 在依赖住入（DI）容器之上实现了它的 <a href="https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/concept-service-locator.md" target="_blank" rel="noopener noreferrer">服务定位器</a>。当一个服务定位器尝试创建一个新的对象实例时，它会把调用转发到 DI 容器。后者将会像前文所述那样自动解决依赖关系。</p>
<blockquote>
<p>💖喜欢本文档的，欢迎点赞、收藏、留言或转发，谢谢支持！<br>
作者邮箱：zhuzixian520@126.com，github地址：<a href="https://github.com/zhuzixian520" target="_blank" rel="noopener noreferrer">github.com/zhuzixian520</a></p>
</blockquote>
</div></template>


